/*
 * Copyright 2008 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.user.client.ui;

import java.util.ArrayList;
import java.util.Collection;

import com.google.gwt.user.client.rpc.IsSerializable;

/**
 * A {@link com.google.gwt.user.client.ui.SuggestOracle} can be used to create suggestions
 * associated with a specific query string. It is currently used by {@link SuggestBox}.
 * 
 * @see SuggestBox
 */
public abstract class SuggestOracle {

	/**
	 * An empty response, used for the default requestDefaultSuggestions.
	 */
	private Response emptyResponse = new Response(new ArrayList<Suggestion>());

	/**
	 * Constructor for {@link com.google.gwt.user.client.ui.SuggestOracle}.
	 */
	public SuggestOracle() {
		super();
	}

	/**
	 * Should {@link Suggestion} display strings be treated as HTML? If true, this all suggestions'
	 * display strings will be interpreted as HTML, otherwise as text.
	 * 
	 * @return by default, returns false
	 */
	public boolean isDisplayStringHTML() {
		return false;
	}

	/**
	 * Generate a {@link Response} based on a default request. The request query must be null as it
	 * represents the results the oracle should return based on no query string.
	 * <p>
	 * After the {@link Response} is created, it is passed into
	 * {@link Callback#onSuggestionsReady(com.google.gwt.user.client.ui.SuggestOracle.Request, com.google.gwt.user.client.ui.SuggestOracle.Response)}
	 * .
	 * </p>
	 * 
	 * @param request the request
	 * @param callback the callback to use for the response
	 */
	public void requestDefaultSuggestions(Request request, Callback callback) {
		assert (request.query == null);
		callback.onSuggestionsReady(request, emptyResponse);
	}

	/**
	 * Generate a {@link Response} based on a specific {@link Request}. After the {@link Response}
	 * is created, it is passed into
	 * {@link Callback#onSuggestionsReady(com.google.gwt.user.client.ui.SuggestOracle.Request, com.google.gwt.user.client.ui.SuggestOracle.Response)}
	 * .
	 * 
	 * @param request the request
	 * @param callback the callback to use for the response
	 */
	public abstract void requestSuggestions(Request request, Callback callback);

	/**
	 * Callback for {@link com.google.gwt.user.client.ui.SuggestOracle}. Every {@link Request}
	 * should be associated with a callback that should be called after a {@link Response} is
	 * generated.
	 */
	public interface Callback {
		/**
		 * Consume the suggestions created by a {@link com.google.gwt.user.client.ui.SuggestOracle}
		 * in response to a {@link Request}.
		 * 
		 * @param request the request
		 * @param response the response
		 */
		void onSuggestionsReady(Request request, Response response);
	}

	/**
	 * A {@link com.google.gwt.user.client.ui.SuggestOracle} request.
	 */
	public static class Request implements IsSerializable {
		private int limit = 20;
		private String query;

		/**
		 * Constructor for {@link Request}.
		 */
		public Request() {
			super();
		}

		/**
		 * Constructor for {@link Request}.
		 * 
		 * @param query the query string
		 */
		public Request(String query) {
			setQuery(query);
		}

		/**
		 * Constructor for {@link Request}.
		 * 
		 * @param query the query string
		 * @param limit limit on the number of suggestions that should be created for this query
		 */
		public Request(String query, int limit) {
			setQuery(query);
			setLimit(limit);
		}

		/**
		 * Gets the limit on the number of suggestions that should be created.
		 * 
		 * @return the limit
		 */
		public int getLimit() {
			return limit;
		}

		/**
		 * Gets the query string.
		 * 
		 * @return the query string
		 */
		public String getQuery() {
			return query;
		}

		/**
		 * Sets the limit on the number of suggestions that should be created.
		 * 
		 * @param limit the limit
		 */
		public void setLimit(int limit) {
			this.limit = limit;
		}

		/**
		 * Sets the query string used for this request.
		 * 
		 * @param query the query string
		 */
		public void setQuery(String query) {
			this.query = query;
		}
	}

	/**
	 * {@link com.google.gwt.user.client.ui.SuggestOracle} response.
	 */
	public static class Response implements IsSerializable {
		private Collection<? extends Suggestion> suggestions;

		/**
		 * Constructor for {@link Response}.
		 */
		public Response() {
			super();
		}

		/**
		 * Constructor for {@link Response}.
		 * 
		 * @param suggestions each element of suggestions must implement the {@link Suggestion}
		 *            interface
		 */
		public Response(Collection<? extends Suggestion> suggestions) {
			setSuggestions(suggestions);
		}

		/**
		 * Gets the collection of suggestions. Each suggestion must implement the {@link Suggestion}
		 * interface.
		 * 
		 * @return the collection of suggestions
		 */
		public Collection<? extends Suggestion> getSuggestions() {
			return this.suggestions;
		}

		/**
		 * Sets the suggestions for this response. Each suggestion must implement the
		 * {@link Suggestion} interface.
		 * 
		 * @param suggestions the suggestions
		 */
		public void setSuggestions(Collection<? extends Suggestion> suggestions) {
			this.suggestions = suggestions;
		}
	}

	/**
	 * Suggestion supplied by the {@link com.google.gwt.user.client.ui.SuggestOracle}. Each
	 * suggestion has a display string and a replacement string. The display string is what is shown
	 * in the SuggestBox's list of suggestions. The interpretation of the display string depends
	 * upon the value of its oracle's {@link SuggestOracle#isDisplayStringHTML()}. The replacement
	 * string is the string that is entered into the SuggestBox's text box when the suggestion is
	 * selected from the list.
	 * <p>
	 * Replacement strings are useful when the display form of a suggestion differs from the input
	 * format for the data. For example, suppose that a company has a webpage with a form which
	 * requires the user to enter the e-mail address of an employee. Since users are likely to know
	 * the name of the employee, a SuggestBox is used to provide name suggestions as the user types.
	 * When the user types the letter <i>f</i>, a suggestion with the display string <i>foo bar</i>
	 * appears. When the user chooses this suggestion, the replacement string,
	 * <i>foobar@company.com</i>, is entered into the SuggestBox's text box.
	 * </p>
	 * <p>
	 * This is an example where the input data format for the suggestion is not as user-friendly as
	 * the display format. In the event that the display of a suggestion exactly matches the input
	 * data format, the <code>Suggestion</code> interface would be implemented in such a way that
	 * the display string and replacement string would be identical.
	 * </p>
	 * <h3>Associating Data Transfer Objects (DTOs) with Suggestion Objects</h3> Some applications
	 * retrieve suggesstions from a server, and may want to send back a DTO with each suggestion. In
	 * the previous example, a DTO returned with the suggestion may provide additional contact
	 * information about the selected employee, and this information could be used to fill out other
	 * fields on the form. To send back a DTO with each suggestion, extend the
	 * <code>Suggestion</code> interface and define a getter method that has a return value of the
	 * DTO's type. Define a class that implements this subinterface and use it to encapsulate each
	 * suggestion.
	 * <p>
	 * To access a suggestion's DTO when the suggestion is selected, add a
	 * {@link com.google.gwt.event.dom.client.ChangeHandler} to the SuggestBox (see SuggestBox's
	 * documentation for more information). In the
	 * <code>SuggestionHandler.onSuggestionSelected(SuggestionEvent event)</code> method, obtain the
	 * selected <code>Suggestion</code> object from the
	 * {@link com.google.gwt.event.dom.client.ChangeHandler} object, and downcast the
	 * <code>Suggestion</code> object to the subinterface. Then, acces the DTO using the DTO getter
	 * method that was defined on the subinterface.
	 * </p>
	 */
	public interface Suggestion {
		/**
		 * Gets the display string associated with this suggestion. The interpretation of the
		 * display string depends upon the value of its oracle's
		 * {@link SuggestOracle#isDisplayStringHTML()}.
		 * 
		 * @return the display string for this suggestion
		 */
		String getDisplayString();

		/**
		 * Gets the replacement string associated with this suggestion. When this suggestion is
		 * selected, the replacement string will be entered into the SuggestBox's text box.
		 * 
		 * @return the string to be entered into the SuggestBox's text box when this suggestion is
		 *         selected
		 */
		String getReplacementString();
	}
}
